﻿//-----------------------------------------------------------------------
// <copyright file="WindowsTerminalServer.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security.Principal;
using FastNET.CleanAct.Client.Win32.Unmanaged;

#endregion

namespace FastNET.CleanAct.Client.Win32
{
    /// <summary>
    /// Win32 Helper class for managing Windows Terminal Services
    /// </summary>
    public static class WindowsTerminalServer
    {
        /// <summary>
        /// Gets a WindowsIdentity for the specified session of the computer
        /// </summary>
        /// <param name="sessionID">SessionID to lookup</param>
        /// <returns>WindowsIdentity with information about the user</returns>
        public static WindowsIdentity GetSessionToken(int sessionID)
        {
            IntPtr l_ptrUserToken;

            //We are supposedly running as a service so we're going to be running in session 0 so get a user token from the active user session
            if (!WTS.WTSQueryUserToken((uint)sessionID, out l_ptrUserToken))
            {
                int l_nLastError = Marshal.GetLastWin32Error();

                //Remember, sometimes nobody is logged in (especially when we're set to Automatically startup) you should get error code 1008 (no user token available)
                if (WTS.ERROR_NO_TOKEN != l_nLastError)
                {
                    //Ensure we're running under the local system account
                    WindowsIdentity l_oIdentity = WindowsIdentity.GetCurrent();

                    if (l_oIdentity != null)
                    {
                        if ("NT AUTHORITY\\SYSTEM" != l_oIdentity.Name)
                        {
                            throw new Exception("The call to WTSQueryUserToken failed and querying the process' account identity results in an identity which does not match 'NT AUTHORITY\\SYSTEM' but instead returns the name:" + l_oIdentity.Name + "  GetLastError returns: " + l_nLastError);
                        }
                    }

                    throw new Exception("The call to WTSQueryUserToken failed, GetLastError returns: " + l_nLastError);
                }
            }

            return new WindowsIdentity(l_ptrUserToken);
        }

        /// <summary>
        /// Retrieves the tokens for all currently logged on sessions
        /// </summary>
        /// <returns>List of WindowsIdentity for all logged on users</returns>
        public static List<WindowsIdentity> GetLoggedOnSessions()
        {
            IntPtr server = WTS.WTSOpenServer("localhost");
            List<WindowsIdentity> returnList = new List<WindowsIdentity>();

            try
            {
                IntPtr ppSessionInfo = IntPtr.Zero;

                Int32 count = 0;
                Int32 retval = WTS.WTSEnumerateSessions(server, 0, 1, ref ppSessionInfo, ref count);
                Int32 dataSize = Marshal.SizeOf(typeof(WTS.WTS_SESSION_INFO));

                Int32 current = (int)ppSessionInfo;

                if (retval != 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        WTS.WTS_SESSION_INFO si = (WTS.WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS.WTS_SESSION_INFO));
                        current += dataSize;

                        returnList.Add(GetSessionToken(si.SessionID));
                    }

                    WTS.WTSFreeMemory(ppSessionInfo);
                }
            }
            finally
            {
                WTS.WTSCloseServer(server);
            }

            return returnList;
        }
    }
}